home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / kernel / ps.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-21  |  47.7 KB  |  1,720 lines  |  [TEXT/KAHL]

  1. /* PostScript(tm) printing for Xconq.
  2.    Copyright (C) 1994, 1995 Massimo Campostrini & Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /*
  10.     Problems
  11.     --------
  12.    Terrain prints reasonably with terrain_gray = 0.5 and
  13. terrain_dither = 0 (at least on a LaserWriter Pro 630 at 600dpi).
  14. terrain_gray = 0 prints terrain icons which are easier to distinguish,
  15. but make many terrains too dark.  Perhaps design new, lighter bitmaps
  16. for terrains?
  17.    Small feature legends are unreadable over dark terrains and connections. 
  18. Legends over terrain improve if terrain_gray is set to 0.75, but then 
  19. terrains are hard to distinguish.  Some kind of masking is in order, at 
  20. least for small legends.
  21.  
  22.     TODO
  23.     ----
  24.    How to mask feature legends?  Use "reverse outline" fonts (from 
  25. "outline.ps" in the cookbook)?  Or just blank out a rectangle under small 
  26. legends, like what is now done for unit names?
  27.    The front end should announce "printing view to file..." and "done"
  28. [done for xconq].
  29.    Implement selection of the region to print.
  30.    Read and interpret `embed' forms.
  31.    Use border/connection bitmaps?  Or differentiate otherwise
  32. between borders or connections?
  33.    Print more fancy legends, e.g., list of side names/emblems 
  34. (on a separate page?)
  35.    Merge parameters better into xconq: initialize by resources and let
  36. each front-end change them (MAC & X should pop-up a window  [X done]).
  37.    If a bitmap is missing but a color image is present, convert it? How?
  38.    Better choice of unit to display (for crowded cells).  How?
  39.    Fix priniting of unit names spanning more than one cell.
  40.    Mark friendly/enemy units.  How?
  41.    More cleanup.
  42.  
  43.     How to customize
  44.     ----------------
  45.    The output look is controlled by a bunch of variables in the
  46. routine init_print.  Their default values are "wired in" into the code,
  47. but xconq may pop-up a configuration window.  Check that the default 
  48. `page_width' and `page_height' fit your paper sheets.
  49.  
  50.    Mail me suggestions and comments.  I would especially appreciate
  51. suggestions for generating cleaner, faster, more compact PostScript.
  52.  
  53.         Massimo Campostrini,
  54. Istituto Nazionale di Fisica Nucleare, Sezione di Pisa,
  55. Piazza Torricelli 2, I56126 Pisa, Italy  ||  Phone: (+39)(50)911272
  56. Internet: campo@sunthpi3.difi.unipi.it   ||  Fax:   (+39)(50)48277  */
  57.  
  58. #include "conq.h"
  59. #include "print.h"
  60. #include "imf.h"
  61. #include <math.h>
  62.  
  63. PrintParameters *pp = NULL;
  64.  
  65. #define on_page(x,y) (((x)>=0) && ((x)<width) && ((y)>=0) && ((y)<height))
  66.  
  67. /* masks for name placing */
  68. #define N_U  '\001'
  69. #define N_D  '\002'
  70. #define N_BK '\020'
  71.  
  72. #define name_at(x,y)  (name_layer[width*((y)+1)+(x)+1])
  73.  
  74. #define GRAY_SCALE 16          /* precision for dithering */
  75. /* length of a hexadecimal string encoding a w*h rectangle */
  76. #define area_leng(w,h) ((((w)+7)/8) * (h) * 2)
  77.  
  78. /* this will allow images up to 64*64 */
  79. #define PSBUFSIZE 1148
  80.  
  81. static int name_dir[] =
  82.   { EAST, WEST, SOUTHEAST, NORTHEAST, SOUTHWEST, NORTHWEST };
  83. /* maximgfam = initial estimate of number of image families in library */
  84. static int maximgfam = 500;
  85. static char *buffer;
  86. static char *escbuf;
  87. static char *padbuf;
  88. static Side *ps_side;
  89. static char zero[] = "00";
  90. static int img_cooked = 0, terr_scale, terr_gray, x0, width, height;
  91. static double ru, rtw, rth, asym, sqrt3,
  92.               cell_grid_width, border_width, connection_width;
  93.  
  94. /* h = hex height; c = vert. distance between hex centers;
  95.    w = hex width = horiz. distance between hex centers.
  96.    For undeformed hexes, c = h*3/4, w = h*sqrt(3)/2;
  97.    the following asymmetry allows 8x8 blocks to tile properly,
  98.    i.e. hex_w%16==0 && hex_c%8==0
  99.    Moreover, the code assumes  hex_w%4==0 && hex_c%2==0 && hex_h%4==0 */
  100. static int hex_h = 32;
  101. static int hex_c = 24;
  102. static int hex_w = 32;
  103. /* unit icon size (compare to hex_w, *not* to hex_h) */
  104. static int unit_w = 16;
  105.  
  106. /* PostScript fonts description */
  107.  
  108. static char *ps_name[3] = {
  109.   "Helvetica-Narrow",
  110.   "Helvetica",
  111.   "Helvetica-Bold"
  112. };
  113.  
  114. /* height of a capital letter in PostScript Helvetica,
  115.    in thousands of the "nominal size" */
  116. static int ps_height = 729;
  117.  
  118. /* width of letters in PostScript Helvetica fonts,
  119.    in thousands of the "nominal size" */
  120. static short ps_width[3][256] = {
  121. /* font 0 is Helvetica-Narrow */
  122.   {
  123.     0,    0,    0,    0,    0,    0,    0,    0,
  124.     0,    0,    0,    0,    0,    0,    0,    0,
  125.     0,    0,    0,    0,    0,    0,    0,    0,
  126.     0,    0,    0,    0,    0,    0,    0,    0,
  127.   228,  228,  291,  456,  456,  729,  547,  182,
  128.   273,  273,  319,  479,  228,  273,  228,  228,
  129.   456,  456,  456,  456,  456,  456,  456,  456,
  130.   456,  456,  228,  228,  479,  479,  479,  456,
  131.   832,  547,  547,  592,  592,  547,  501,  638,
  132.   592,  228,  410,  547,  456,  683,  592,  638,
  133.   547,  638,  592,  547,  501,  592,  547,  774,
  134.   547,  547,  501,  228,  228,  228,  385,  456,
  135.   182,  456,  456,  410,  456,  456,  228,  456,
  136.   456,  182,  182,  410,  182,  683,  456,  456,
  137.   456,  456,  273,  410,  228,  456,  410,  592,
  138.   410,  410,  410,  274,  213,  274,  479,    0,
  139.     0,    0,    0,    0,    0,    0,    0,    0,
  140.     0,    0,    0,    0,    0,    0,    0,    0,
  141.     0,    0,    0,    0,    0,    0,    0,    0,
  142.     0,    0,    0,    0,    0,    0,    0,    0,
  143.     0,  273,  456,  456,  137,  456,  456,  456,
  144.   456,  157,  273,  456,  273,  273,  410,  410,
  145.     0,  456,  456,  456,  228,    0,  440,  287,
  146.   182,  273,  273,  456,  820,  820,    0,  501,
  147.     0,  273,  273,  273,  273,  273,  273,  273,
  148.   273,    0,  273,  273,    0,  273,  273,  273,
  149.   820,    0,    0,    0,    0,    0,    0,    0,
  150.     0,    0,    0,    0,    0,    0,    0,    0,
  151.     0,  820,    0,  303,    0,    0,    0,    0,
  152.   456,  638,  820,  299,    0,    0,    0,    0,
  153.     0,  729,    0,    0,    0,  228,    0,    0,
  154.   182,  501,  774,  501,    0,    0,    0,    0 },
  155. /* font 1 is Helvetica */
  156.   {
  157.     0,    0,    0,    0,    0,    0,    0,    0,
  158.     0,    0,    0,    0,    0,    0,    0,    0,
  159.     0,    0,    0,    0,    0,    0,    0,    0,
  160.     0,    0,    0,    0,    0,    0,    0,    0,
  161.   278,  278,  355,  556,  556,  889,  667,  222,
  162.   333,  333,  389,  584,  278,  333,  278,  278,
  163.   556,  556,  556,  556,  556,  556,  556,  556,
  164.   556,  556,  278,  278,  584,  584,  584,  556,
  165.  1015,  667,  667,  722,  722,  667,  611,  778,
  166.   722,  278,  500,  667,  556,  833,  722,  778,
  167.   667,  778,  722,  667,  611,  722,  667,  944,
  168.   667,  667,  611,  278,  278,  278,  469,  556,
  169.   222,  556,  556,  500,  556,  556,  278,  556,
  170.   556,  222,  222,  500,  222,  833,  556,  556,
  171.   556,  556,  333,  500,  278,  556,  500,  722,
  172.   500,  500,  500,  334,  260,  334,  584,    0,
  173.     0,    0,    0,    0,    0,    0,    0,    0,
  174.     0,    0,    0,    0,    0,    0,    0,    0,
  175.     0,    0,    0,    0,    0,    0,    0,    0,
  176.     0,    0,    0,    0,    0,    0,    0,    0,
  177.     0,  333,  556,  556,  167,  556,  556,  556,
  178.   556,  191,  333,  556,  333,  333,  500,  500,
  179.     0,  556,  556,  556,  278,    0,  537,  350,
  180.   222,  333,  333,  556, 1000, 1000,    0,  611,
  181.     0,  333,  333,  333,  333,  333,  333,  333,
  182.   333,    0,  333,  333,    0,  333,  333,  333,
  183.  1000,    0,    0,    0,    0,    0,    0,    0,
  184.     0,    0,    0,    0,    0,    0,    0,    0,
  185.     0, 1000,    0,  370,    0,    0,    0,    0,
  186.   556,  778, 1000,  365,    0,    0,    0,    0,
  187.     0,  889,    0,    0,    0,  278,    0,    0,
  188.   222,  611,  944,  611,    0,    0,    0,    0 },
  189. /* font 2 is Helvetica-Bold */
  190.   {
  191.     0,    0,    0,    0,    0,    0,    0,    0,
  192.     0,    0,    0,    0,    0,    0,    0,    0,
  193.     0,    0,    0,    0,    0,    0,    0,    0,
  194.     0,    0,    0,    0,    0,    0,    0,    0,
  195.   278,  333,  474,  556,  556,  889,  722,  278,
  196.   333,  333,  389,  584,  278,  333,  278,  278,
  197.   556,  556,  556,  556,  556,  556,  556,  556,
  198.   556,  556,  333,  333,  584,  584,  584,  611,
  199.   975,  722,  722,  722,  722,  667,  611,  778,
  200.   722,  278,  556,  722,  611,  833,  722,  778,
  201.   667,  778,  722,  667,  611,  722,  667,  944,
  202.   667,  667,  611,  333,  278,  333,  584,  556,
  203.   278,  556,  611,  556,  611,  556,  333,  611,
  204.   611,  278,  278,  556,  278,  889,  611,  611,
  205.   611,  611,  389,  556,  333,  611,  556,  778,
  206.   556,  556,  500,  389,  280,  389,  584,    0,
  207.     0,    0,    0,    0,    0,    0,    0,    0,
  208.     0,    0,    0,    0,    0,    0,    0,    0,
  209.     0,    0,    0,    0,    0,    0,    0,    0,
  210.     0,    0,    0,    0,    0,    0,    0,    0,
  211.     0,  333,  556,  556,  167,  556,  556,  556,
  212.   556,  238,  500,  556,  333,  333,  611,  611,
  213.     0,  556,  556,  556,  278,    0,  556,  350,
  214.   278,  500,  500,  556, 1000, 1000,    0,  611,
  215.     0,  333,  333,  333,  333,  333,  333,  333,
  216.   333,    0,  333,  333,    0,  333,  333,  333,
  217.  1000,    0,    0,    0,    0,    0,    0,    0,
  218.     0,    0,    0,    0,    0,    0,    0,    0,
  219.     0, 1000,    0,  370,    0,    0,    0,    0,
  220.   611,  778, 1000,  365,    0,    0,    0,    0,
  221.     0,  889,    0,    0,    0,  278,    0,    0,
  222.   278,  611,  944,  611,    0,    0,    0,    0 }
  223. };
  224.  
  225. typedef struct a_ps_image_family {
  226.   char *name;
  227.   ImageFamily *imf;
  228.   Image *img;
  229.   char *hexmonodata;
  230.   char *hexmaskdata;
  231. } PsImage;
  232.  
  233. static PsImage *unit_icon, *terr_icon, *side_icon;
  234.  
  235. static int conn_x[NUMDIRS], conn_y[NUMDIRS],
  236.            hexc_x[NUMDIRS+1], hexc_y[NUMDIRS+1];
  237.  
  238. /* prototypes */
  239.  
  240. void ps_cook_imf PROTO ((void));
  241. void ps_initialize_imf PROTO ((PsImage *psim));
  242. char *ps_hex_dump PROTO ((Obj *data, int w, int h));
  243. char *img_untile PROTO ((char *tiled, int w, int h, int istile,
  244.              int dither, int scale, int gray));
  245. void misc_init PROTO ((void));
  246. void ps_initialize PROTO ((FILE *fp));
  247. void page_init PROTO ((FILE *fp, int page, int i, int j, char *stime));
  248. int nearest_valid_x PROTO((int x, int y));
  249. int seen_terrain_at PROTO ((int x, int y, Side *side));
  250. Unit *seen_unit_at PROTO ((int x, int y, Side *side));
  251. int seen_utype_at PROTO ((int x, int y, Side *side));
  252. int sideno_of_seen_unit_at PROTO ((int x, int y, Side *side));
  253. char *name_of_seen_unit_at PROTO ((int x, int y, Side *side));
  254. char *summary_of_seen_units_at PROTO ((int x, int y, Side *side));
  255. int ps_string_width PROTO ((char *str, int font));
  256. void place_feature_legends
  257.   PROTO ((Legend *leg, int nf, Side *side, int orient, int block));
  258. int blocking_utype PROTO ((int u, int block));
  259. int num_features PROTO ((void));
  260. int print_unit_legends PROTO ((FILE *fp, char *name, char *summary, char *m,
  261.     int dir, int cx, int cy));
  262.  
  263. void
  264. ps_cook_imf()
  265. {
  266.     int i;
  267.     Side *s;
  268.     Image *img;
  269.     
  270.     if (buffer == NULL)
  271.       buffer = xmalloc(PSBUFSIZE + 4);
  272.     if (unit_icon == NULL)
  273.       unit_icon = (PsImage *)
  274.     xmalloc (numutypes * sizeof(PsImage));
  275.     if (terr_icon == NULL)
  276.       terr_icon = (PsImage *)
  277.     xmalloc (numttypes * sizeof(PsImage));
  278.     if (side_icon == NULL)
  279.       side_icon = (PsImage *)
  280.     xmalloc (MAXSIDES  * sizeof(PsImage));
  281.     
  282.     /* pick the images we need */
  283.     for_all_unit_types(i) {
  284.     unit_icon[i].imf = NULL;
  285.     /* first choice */
  286.     if (utypes[i].imagename) {
  287.         unit_icon[i].name = utypes[i].imagename;
  288.         unit_icon[i].imf = find_imf(utypes[i].imagename);
  289.     }
  290.     /* second choice */
  291.     if (!unit_icon[i].imf && utypes[i].name) {
  292.         unit_icon[i].name = utypes[i].name;
  293.         unit_icon[i].imf = find_imf(utypes[i].name);
  294.     }
  295.  
  296.     if (!unit_icon[i].imf || !unit_icon[i].imf->images) {
  297.         unit_icon[i].img = NULL;
  298.     } else {
  299.         unit_icon[i].img = unit_icon[i].imf->images;
  300.         for (img=unit_icon[i].imf->images->next; img; img=img->next) {
  301.         /* should we prefer the new image? */
  302.         if ((!unit_icon[i].img->monodata && img->monodata) ||
  303.             (!unit_icon[i].img->maskdata && img->maskdata) ||
  304.             ( unit_icon[i].img->w < img->w)) {
  305.             unit_icon[i].img = img;
  306.         }
  307.         }
  308.     }
  309.     ps_initialize_imf(&unit_icon[i]);
  310.     }
  311.     
  312.     for_all_terrain_types(i) {
  313.     terr_icon[i].imf = NULL;
  314.     /* first choice */
  315.     if (ttypes[i].imagename) {
  316.         terr_icon[i].name = ttypes[i].imagename;
  317.         terr_icon[i].imf = find_imf(ttypes[i].imagename);
  318.     }
  319.     /* second choice */
  320.     if (!terr_icon[i].imf && ttypes[i].name) {
  321.         terr_icon[i].name = ttypes[i].name;
  322.         terr_icon[i].imf = find_imf(ttypes[i].name);
  323.     }
  324.     if (terr_icon[i].imf) {
  325.         terr_icon[i].img = terr_icon[i].imf->images;
  326.     } else {
  327.         terr_icon[i].img = NULL;
  328.     }
  329.     ps_initialize_imf(&terr_icon[i]);
  330.     }
  331.     
  332.     i = -1;
  333.     for_all_sides_plus_indep(s) {
  334.     i++;
  335.     side_icon[i].imf = NULL;
  336.     /* first choice */
  337.     if (s->emblemname) {
  338.         side_icon[i].name = s->emblemname;
  339.         side_icon[i].imf = find_imf(s->emblemname);
  340.     }
  341.     if (side_icon[i].imf) {
  342.         side_icon[i].img = side_icon[i].imf->images;
  343.     } else {
  344.         side_icon[i].img = NULL;
  345.     }
  346.     ps_initialize_imf(&side_icon[i]);
  347.     }
  348.     
  349.     img_cooked = 1;
  350. }
  351.  
  352. void 
  353. ps_initialize_imf(psim)
  354. PsImage *psim;
  355. {
  356.     /* checks & cleanup */
  357.     if (!psim)
  358.       return;
  359.     if (!psim->imf)
  360.       psim->img = NULL;
  361.  
  362.     if (!psim->img) {
  363.     psim->hexmonodata = NULL;
  364.     psim->hexmaskdata = NULL;
  365.     return;
  366.     }
  367.     
  368.     psim->hexmonodata =
  369.       ps_hex_dump(psim->img->monodata, psim->img->w, psim->img->h);
  370.     psim->hexmaskdata =
  371.       ps_hex_dump(psim->img->maskdata, psim->img->w, psim->img->h);
  372. }
  373.  
  374. char *
  375. ps_hex_dump(data, w, h)
  376. Obj *data;
  377. int w, h;
  378. {
  379.     int i, numbytes;
  380.     unsigned int u;
  381.     char *hexdata, *c;
  382.   
  383.     numbytes = h * ((w + 7) / 8);
  384.     interp_bytes(data, numbytes, buffer, 0);
  385.     c = hexdata = xmalloc(2*numbytes+2);
  386.     for (i = 0; i < numbytes; ++i) {
  387.     u = buffer[i] & 0xff;
  388.     sprintf(c, "%2.2x", u);
  389.     c += 2;
  390.     }
  391.     *c = '\0';
  392.     return hexdata;
  393. }
  394.  
  395. /* Should use two symbolic constants instead of 8 ? */
  396.  
  397. char *
  398. img_untile(tiled, w, h, istile, dither, scale, gray)
  399. char *tiled;
  400. int w, h, istile, dither, scale, gray;
  401. {
  402.     int dx, dy, x, y, xo, yo, xi, yi, tw, th;
  403.     unsigned int u;
  404.     char mask[8][8], str[12], *b;
  405.  
  406.     if (tiled == NULL)
  407.       return zero;
  408.     if (!istile)
  409.       return tiled;
  410.  
  411.     if (strlen(tiled) != area_leng(8,8))
  412.       return zero;
  413.   
  414.     /* untile image into hexagon */
  415.  
  416.     /* width and height of the four triangles we have to cut off
  417.        a hex_w*hex_h rectangle to form a hexagon */
  418.     tw = hex_w / 2;
  419.     th = hex_h - hex_c;
  420.  
  421.     /* convert to binary */
  422.     b = tiled;
  423.     for (y = 0; y < 8; y++) {
  424.     for (x = 0; x < 8; x++) {
  425.         if ((x & 0x3) == 0) {
  426.         str[0] = *b++;
  427.         str[1] = '\0';
  428.         sscanf(str, "%x", &u);
  429.         }
  430.         mask[x][y] = (u & 0x8) >> 3;
  431.         u = u << 1;
  432.     }
  433.     }
  434.  
  435.     if (area_leng(scale * hex_w, scale * hex_h) > PSBUFSIZE) {
  436.     notify(ps_side, "buffer size is %d, too small to load %dx%d image",
  437.            PSBUFSIZE, scale * hex_w, scale * hex_h);
  438.     return zero;
  439.     }
  440.  
  441.     /* Convert to hexadecimal. */
  442.     b = buffer;
  443.     for (yo = 0, y = 0; yo < hex_h; yo++) {
  444.     dy = min(yo, hex_h - 1 - yo);
  445.     for (yi = 0; yi < scale; yi++, y++) {
  446.         u = 0;
  447.         for (xo = 0, x = 0; xo < hex_w; xo++) {
  448.         dx = min(xo, hex_w - 1 - xo);
  449.         for (xi = 0; xi < scale; xi++, x++) {
  450.             u = u << 1;
  451.             /* is the center of the (xo,yo) cell inside the hexagon? */
  452.             if (th * dx + tw * dy >= th * tw - (th + tw) / 2) {
  453.             u |= mask[x & 0x7][y & 0x7]
  454.               && xrandom(GRAY_SCALE) >= gray;
  455.             }
  456.             if ((x & 0x3) == 0x3 || x == scale * hex_w - 1) {
  457.             sprintf(b++, "%1.1x", u);
  458.             u = 0;
  459.             }
  460.         }
  461.         }
  462.         /* does the hexadecimal string need padding? */
  463.         if (((scale * hex_w - 1) & 7) < 4) {
  464.         *b++ = '0';
  465.         }
  466.     }
  467.     }
  468.  
  469.     *b = '\0';
  470.     if (strlen(buffer) != area_leng(scale * hex_w, scale * hex_h)) {
  471.     notify(ps_side, "length (%s) is %d, expecting %d",
  472.            buffer, strlen(buffer),
  473.            area_leng(scale * hex_w, scale * hex_h));
  474.     }
  475.     return buffer;
  476. }
  477.  
  478. /* Convert to PostScript escape sequences:
  479.    '\' to '\\', '(' to '\(', ')' to '\)'.  */
  480.  
  481. char *
  482. add_esc_string(str)
  483. char *str;
  484. {
  485.     char *ps, *pb;
  486.  
  487.     if (escbuf == NULL)
  488.       escbuf = xmalloc(BUFSIZE);
  489.     for (ps = str, pb = escbuf; *ps && pb < escbuf + BUFSIZE - 2; ps++, pb++) {
  490.     if (*ps == '(' || *ps == ')' || *ps == '\\')
  491.       *pb++ = '\\';
  492.     *pb = *ps;
  493.     }
  494.     *pb = '\0';
  495.     return escbuf;
  496. }
  497.  
  498.  
  499. char *
  500. pad_blanks(str, n)
  501. char *str;
  502. int n;
  503. {
  504.     char *pb;
  505.     int i;
  506.   
  507.     if (padbuf == NULL)
  508.       padbuf = xmalloc(BUFSIZE);
  509.     pb = padbuf;
  510.     while (*str && pb < padbuf + BUFSIZE - 2) {
  511.     *pb++ = *str++;
  512.     if (*str) {
  513.         for (i = 0; i < n; i++)
  514.           *pb++ = ' ';
  515.     }
  516.     }
  517.     *pb = '\0';
  518.     return padbuf;
  519. }
  520.  
  521. /* set parameter defaults */
  522. /* all length are in PostScript units (1/72 in)  */
  523.  
  524. void
  525. init_ps_print(ipp)
  526. PrintParameters *ipp;
  527. {
  528.     double in, cm;
  529.     
  530.     if (pp == NULL) {
  531.     pp = (PrintParameters *) xmalloc(sizeof(PrintParameters));
  532.  
  533.     /* the following code must be executed once,
  534.        before calling the parameter selection front-end */
  535.     in = 72.0;
  536.     cm = in/2.54;
  537.  
  538.     /*  US, letter sheet  */
  539.     pp->page_width =    8.5*in;
  540.     pp->page_height =  11.0*in;
  541.     pp->cm = 0;        /* use inches in front-end */
  542.  
  543.     /*  metric world, A4 sheet  */
  544.     pp->page_width =   21.0*cm;
  545.     pp->page_height =  29.7*cm;
  546.     pp->cm = 1;        /* use centimeters in front-end */
  547.  
  548.     /*  title and coordinates are printed in the "margins":
  549.         make room in top_margin, left_margin and right_margin  */
  550.     pp->top_margin =    3.0*cm;
  551.     pp->bottom_margin = 2.0*cm;
  552.     pp->left_margin =   2.0*cm;
  553.     pp->right_margin =  2.0*cm;
  554.  
  555.     pp->cell_size        = 0.500*cm;
  556.     pp->cell_grid_width  = 0.005*cm; /* 0 or negative to avoid hex borders */
  557.     pp->border_width     = 0.050*cm;
  558.     pp->connection_width = 0.025*cm;
  559.  
  560.     pp->features  =  1;    /* print geographical features */
  561.  
  562.     /*  names = 0:  don't print unit names;
  563.         names = 1:  filter unit names;
  564.         names = 2:  print all unit names.  */
  565.     pp->names = 1;
  566.     pp->cell_summary = 1;    /* print summary of units in cell */
  567.     pp->corner_coord   = 1;    /* print coordinates of map corners */
  568.     pp->terrain_gray   = 0.50; /* gray level for terrain;  0 = black */
  569.     pp->enemy_gray     = 0.50; /* gray level for enemies;  0 = black */
  570.     pp->terrain_dither = 0;    /* make terrain gray by dithering;
  571.                    0 = use PostScript "setgray" operator */
  572.     pp->terrain_double = 1;    /* make mesh finer when dithering */
  573.     }
  574.     
  575.     if (ipp) {
  576.     *ipp = *pp;
  577.     }
  578. }
  579.  
  580. /* initialization of variables */
  581.  
  582. void 
  583. misc_init()
  584. {
  585.     if (pp->terrain_dither) {
  586.     terr_gray = GRAY_SCALE*pp->terrain_gray + 0.5;
  587.     terr_scale = (pp->terrain_double && terr_gray>0 &&
  588.               terr_gray<GRAY_SCALE) ? 2 : 1;
  589.     } else {
  590.     terr_gray = 0;
  591.     terr_scale = 1;
  592.     }
  593.     ru = 1.0 / unit_w;
  594.     rtw = 1.0 / hex_w;
  595.     rth = 1.0 / hex_h;
  596.     sqrt3 = 1.73205080756888;    /* sqrt(3.0);  hardwired to avoid need of -lm */
  597.     asym = hex_c / (0.5 * sqrt3 * hex_w);
  598.     
  599.     x0 = area.xwrap ? 0 : (area.height+2)/4;
  600.     
  601.     /* from a hex center to neighbor hex centers */
  602.     conn_x[0] =  hex_w/2;        conn_y[0] =  hex_c;
  603.     conn_x[1] =  hex_w;        conn_y[1] =  0;
  604.     conn_x[2] =  hex_w/2;        conn_y[2] = -hex_c;
  605.     conn_x[3] = -hex_w/2;        conn_y[3] = -hex_c;
  606.     conn_x[4] = -hex_w;        conn_y[4] =  0;
  607.     conn_x[5] = -hex_w/2;        conn_y[5] =  hex_c;
  608.     
  609.     /* from a hex center to hex vertices (with wraparound) */
  610.     hexc_x[0] =  0;        hexc_y[0] =  hex_h/2;
  611.     hexc_x[1] =  hex_w/2;        hexc_y[1] =  hex_h/4;
  612.     hexc_x[2] =  hex_w/2;        hexc_y[2] = -hex_h/4;
  613.     hexc_x[3] =  0;        hexc_y[3] = -hex_h/2;
  614.     hexc_x[4] = -hex_w/2;        hexc_y[4] = -hex_h/4;
  615.     hexc_x[5] = -hex_w/2;        hexc_y[5] =  hex_h/4;
  616.     hexc_x[6] =  0;        hexc_y[6] =  hex_h/2;
  617.     
  618.     cell_grid_width  = pp->cell_grid_width  * hex_w/pp->cell_size;
  619.     border_width     = pp->border_width     * hex_w/pp->cell_size;
  620.     connection_width = pp->connection_width * hex_w/pp->cell_size;
  621.     
  622.     /* rows per page and cells per row */
  623.     height = (pp->page_height - pp->top_margin - pp->bottom_margin)
  624.       * 2 / (sqrt3 * pp->cell_size);
  625.     width  = (pp->page_width - pp->left_margin - pp->right_margin)
  626.       / pp->cell_size;
  627. }
  628.  
  629. /* write prolog and macro definitions to view file */
  630. void
  631. ps_initialize(fp)
  632. FILE *fp;
  633. {
  634.     int i;
  635.     Image *img;
  636.     Side *s;
  637.     
  638.     /*  PostScript prolog  */
  639.     fprintf(fp, "%%!PS-Adobe-2.0\n");
  640.     fprintf(fp, "%%%%BoundingBox: %d %d %d %d\n",
  641.         (int) pp->left_margin,
  642.         (int) pp->top_margin,
  643.         (int) (pp->page_width - pp->left_margin),
  644.         (int) (pp->page_height - pp->top_margin));
  645.     fprintf(fp, "%%%%Creator: Xconq version %s\n", version_string());
  646.     
  647. #ifdef UNIX
  648.     {
  649.     char *userp, *hostp;
  650.  
  651.     userp = getenv("USER");
  652.     hostp = getenv("HOST");
  653.     if (userp != 0) {
  654.         if (hostp != 0) {
  655.         fprintf(fp, "%%%%For: %s@%s\n", userp, hostp);
  656.         } else {
  657.         fprintf(fp, "%%%%For: %s\n", userp);
  658.         }
  659.     }
  660.     }
  661. #endif /* UNIX */
  662. #ifdef MAC
  663.     /* ??? */
  664. #endif /* MAC */
  665.     
  666.     fprintf(fp, "%%%%DocumentFonts:\
  667.  Helvetica Helvetica-Narrow Helvetica-Bold Times-Bold\n");
  668.     fprintf(fp, "%%%%Pages: (atend)\n");
  669.     fprintf(fp, "%%%%EndComments\n");
  670.     
  671.     /* PostScript definitions */
  672.     
  673.     /* new line */
  674.     fprintf(fp, "/nl { 0 %d translate } bind def\n", -hex_c);
  675.     /* backspace */
  676.     fprintf(fp, "/bs { %d 0 translate } bind def\n", -hex_w);
  677.     /* space */
  678.     fprintf(fp, "/sp { %d 0 translate } bind def\n", hex_w);
  679.     /* multiple spaces */
  680.     fprintf(fp, "/msp { %d mul 0 translate } bind def\n", hex_w);
  681.     /* half space */
  682.     fprintf(fp, "/hs { %d 0 translate } bind def\n", hex_w / 2);
  683.     /* show string right of current position */
  684.     fprintf(fp, "/rshow { show } bind def\n");
  685.     /* show string centered on current position */
  686.     fprintf(fp, "/cshow { dup stringwidth pop -0.5 mul 0 rmoveto");
  687.     fprintf(fp, " show } bind def\n");
  688.     /* show string left of current position, correcting asymmetry */
  689.     fprintf(fp, "/lshow { dup stringwidth pop neg 0 rmoveto");
  690.     fprintf(fp, " show } bind def\n");
  691.     /* draw hexagon (if requested) */
  692.     if (cell_grid_width > 0) {
  693.     fprintf(fp, "/hex { currentgray 0 setgray 0 %d moveto %d 0 lineto %d %d",
  694.         hex_c-hex_h, hex_w/2, hex_w,hex_c-hex_h);
  695.     fprintf(fp, " lineto %d %d lineto %d %d lineto 0 %d lineto 0 %d lineto",
  696.         hex_w,-hex_c, hex_w/2,-hex_h, -hex_c, hex_c-hex_h);
  697.     fprintf(fp, " stroke setgray } bind def\n");
  698.     } else {
  699.     fprintf(fp, "/hex { } bind def\n");
  700.     }
  701.     /* draw a line */
  702.     fprintf(fp, "/li { moveto lineto stroke } bind def\n");
  703.     
  704.     /* print unit name below unit icon */
  705.     fprintf(fp, "/n { %d %d moveto sbc } bind def\n", hex_w/2, 1-hex_h);
  706.     
  707.     /* print left-justified in upper section of hex  */
  708.     fprintf(fp, "/nlu { 0 %d moveto sbl } bind def\n", -hex_h/2);
  709.     /* print left-justified in lower section of hex  */
  710.     fprintf(fp, "/nld { 0 %d moveto sbl } bind def\n", -(hex_h+hex_c)/2);
  711.     /* print right-justified in upper section of hex  */
  712.     fprintf(fp, "/nru { %d %d moveto sbr } bind def\n", hex_w, -hex_h/2);
  713.     /* print right-justified in lower corner of hex  */
  714.     fprintf(fp, "/nrd { %d %d moveto sbr } bind def\n",
  715.         hex_w, -(hex_h+hex_c)/2);
  716.     
  717.     /* print a centered string, blanking out a bounding rectangle */
  718.     fprintf(fp, "/sbc { dup stringwidth gsave ");
  719.     fprintf(fp, "0 -0.5 rmoveto pop dup 0.5 add 7 scale -0.5 0 rmoveto ");
  720.     fprintf(fp,
  721.         "1 0 rlineto 0 1 rlineto -1 0 rlineto 0 -1 rlineto 1 setgray fill ");
  722.     fprintf(fp, "grestore -0.5 mul 0.5 rmoveto show } bind def\n");
  723.     /* print a left-aligned string, blanking out a bounding rectangle */
  724.     fprintf(fp, "/sbl { dup stringwidth gsave ");
  725.     fprintf(fp, "0 -0.5 rmoveto pop 0.5 add 7 scale ");
  726.     fprintf(fp,
  727.         "1 0 rlineto 0 1 rlineto -1 0 rlineto 0 -1 rlineto 1 setgray fill ");
  728.     fprintf(fp, "grestore 0 0.5 rmoveto show } bind def\n");
  729.     /* print a right-aligned string, blanking out a bounding rectangle */
  730.     fprintf(fp, "/sbr { dup stringwidth gsave ");
  731.     fprintf(fp, "0 -0.5 rmoveto pop dup 0.5 add 7 scale -1 0 rmoveto ");
  732.     fprintf(fp,
  733.         "1 0 rlineto 0 1 rlineto -1 0 rlineto 0 -1 rlineto 1 setgray fill ");
  734.     fprintf(fp, "grestore neg 0.5 rmoveto show } bind def\n");
  735.     
  736.     /* draw a unit */
  737.     for_all_unit_types(i) {
  738.     if (!unit_icon[i].hexmonodata) {
  739.         fprintf(fp, "%% icon for unit type %s not found\n",
  740.             utypes[i].name);
  741.         fprintf(fp, "/u%X { } def\n", i);
  742.     } else {
  743.         img = unit_icon[i].img;
  744.         fprintf(fp, "%% unit type %s\n", utypes[i].name);
  745.         /* center unit icon into hex */
  746.         fprintf(fp, "/u%X { gsave %d %d translate",
  747.             i, (hex_w-unit_w)/2, -(hex_h-unit_w)/2-3);
  748.         if (unit_icon[i].hexmaskdata) {
  749.         fprintf(fp, " 1 setgray %d %d true [ %g 0 0 %g 0 0 ]",
  750.             img->w, img->h, img->w*ru, -img->h*ru);
  751.         fprintf(fp, " {<%s>} imagemask",
  752.             unit_icon[i].hexmaskdata);
  753.         }
  754.         fprintf(fp, " 0 setgray %d %d true [ %g 0 0 %g 0 0 ]",
  755.             img->w, img->h, img->w*ru, -img->h*ru);
  756.         fprintf(fp, " {<%s>} imagemask grestore } def\n",
  757.             unit_icon[i].hexmonodata);
  758.     }
  759.     }
  760.     
  761.     /* draw a terrain call and a hex */
  762.     for_all_terrain_types(i) {
  763.     if (!terr_icon[i].hexmonodata) {
  764.         fprintf(fp, "%% icon for terrain type %s not found\n",
  765.             ttypes[i].name);
  766.         fprintf(fp, "/t%X { hex sp } def\n", i);
  767.     } else {
  768.         int w, h;
  769.  
  770.         img = terr_icon[i].img;
  771.         w = img->w;
  772.         h = img->h;
  773.         if (img->istile && w==8 && h==8) {
  774.         w = terr_scale*hex_w;  h = terr_scale*hex_h;
  775.         }
  776.         fprintf(fp, "%% terrain type %s\n", ttypes[i].name);
  777.         fprintf(fp, "/t%X { %d %d true [ %g 0 0 %g 0 0 ]",
  778.             i, w, h, w*rtw, -h*rth);
  779.         fprintf(fp, " {<%s>} imagemask hex sp } def\n",
  780.             img_untile(terr_icon[i].hexmonodata, w, h, img->istile,
  781.                    pp->terrain_dither, terr_scale, terr_gray));
  782.     }
  783.     }
  784.     
  785.     /* draw an emblem */
  786.     i = 0;
  787.     for_all_sides_plus_indep(s) {
  788.     if (!side_icon[i].hexmonodata) {
  789.         fprintf(fp, "%% icon for side %s not found\n", side_name(s));
  790.         fprintf(fp, "/s%X { } def\n", side_number(s));
  791.     } else {
  792.         img = side_icon[i].img;
  793.         fprintf(fp, "%% side %s\n", side_name(s));
  794.         /* place side icon into upper corner of hex */
  795.         fprintf(fp, "/s%X { gsave %g %g translate",
  796.             side_number(s), 0.5 * (hex_w - img->w), -4 / asym);
  797.         if (side_icon[i].hexmaskdata) {
  798.         fprintf(fp, " 1 setgray %d %d true [ 1 0 0 -1 0 0 ]", img->w, img->h);
  799.         fprintf(fp, " {<%s>} imagemask",
  800.             side_icon[i].hexmaskdata);
  801.         }
  802.         fprintf(fp, " 0 setgray %d %d true [ 1 0 0 -1 0 0 ]", img->w, img->h);
  803.         fprintf(fp, " {<%s>} imagemask grestore } def\n",
  804.             side_icon[i].hexmonodata);
  805.     }
  806.     i++;
  807.     }
  808.     
  809.     fprintf(fp, "%%%%EndProlog\n");
  810.     fprintf(fp, "\n");
  811. }
  812.  
  813. /*  initialize page  */
  814.  
  815. void 
  816. page_init(fp, page, i, j, stime)
  817. FILE *fp;
  818. int page, i, j;
  819. char *stime;
  820. {
  821.     int x, xx, y, y0, nx, ny;
  822.     char *gametitle;
  823.     
  824.     fprintf(fp, "%%%%Page: %d %d\n", page, page);
  825.     fprintf(fp, "%g %g translate ",
  826.         pp->left_margin, pp->page_height - pp->top_margin);
  827.     fprintf(fp, "%g %g scale\n", pp->cell_size / hex_w, pp->cell_size / hex_w);
  828.     
  829.     /* print page title */
  830.     gametitle = NULL;
  831.     if (mainmodule != NULL) {
  832.     if (mainmodule->title != NULL)
  833.       gametitle = mainmodule->title;
  834.     else if (mainmodule->name != NULL)
  835.       gametitle = mainmodule->name;
  836.     }
  837.     
  838.     /* "20" shouldn't be wired in... */
  839.     fprintf(fp, "/Times-Bold findfont 16 scalefont setfont\n");
  840.     
  841.     fprintf(fp, "%g 20 moveto (%s) rshow\n", 0.05 * width * hex_w,
  842.         add_esc_string(stime));
  843.     
  844.     fprintf(fp, "%g 20 moveto (Xconq", 0.5 * width * hex_w);
  845.     if (gametitle != NULL) {
  846.     fprintf(fp, ":  %s", add_esc_string(gametitle));
  847.     }
  848.     fprintf(fp, "  \\(%s\\)) cshow\n",
  849.         add_esc_string(absolute_date_string(g_turn())));
  850.     
  851.     fprintf(fp, "%g 20 moveto (Page %d) lshow\n", 0.95 * width * hex_w, page);
  852.     
  853.     /*  print map corner coordinates? */
  854.     if (pp->corner_coord) {
  855.     fprintf(fp, "/Helvetica findfont 12 scalefont setfont\n");
  856.     
  857.     y0 = y = area.height - 1 - i * height;
  858.     x = j * width - (y+1) / 2 + x0;
  859.     x = wrapx(x);
  860.     xx = nearest_valid_x(x, y) - x;
  861.     x += xx;
  862.     fprintf(fp, "%d %g moveto (%d,%d) lshow\n",
  863.         (2 * xx - 1) * hex_w / 2, 0 / asym, x, y);
  864.     
  865.     nx = min(width, area.width-j*width);
  866.     x = j * width + nx - 1 - (y + 1) / 2 + x0;
  867.     x = wrapx(x);
  868.     xx = nearest_valid_x(x, y) - x;
  869.     x += xx;
  870.     fprintf(fp, "%d %g moveto (%d,%d) rshow\n",
  871.         (2 * (nx + xx) + 1) * hex_w / 2, 0 / asym, x, y);
  872.     
  873.     y = max(area.height - (i + 1) * height, 0);
  874.     ny = y0 - y;
  875.     x = j * width - (y + 1) / 2 + x0;
  876.     x = wrapx(x);
  877.     xx = nearest_valid_x(x, y) - x;
  878.     x += xx;
  879.     fprintf(fp, "%d %g moveto (%d,%d) lshow\n",
  880.         (2 * xx - 1) * hex_w / 2, -(hex_c * (ny + 2) + 3) / asym,
  881.         x, y);
  882.     
  883.     x = j * width + nx - 1 - (y + 1) / 2 + x0;
  884.     x = wrapx(x);
  885.     xx = nearest_valid_x(x, y) - x;
  886.     x += xx;
  887.     fprintf(fp, "%d %g moveto (%d,%d) rshow\n",
  888.         (2 * (nx + xx) + 1) * hex_w / 2,
  889.         -(hex_c * (ny + 2) +3 ) / asym, x, y);
  890.     }
  891. }
  892.  
  893. int
  894. nearest_valid_x(x, y)
  895. int x, y;
  896. {
  897.     int xlo, xhi;
  898.  
  899.     if (area.xwrap)
  900.       return x;
  901.     xlo = max(0, area.height / 2 - y);
  902.     xhi = min(area.width-1, area.width + area.height / 2 - 1 - y);
  903.     x = max(x, xlo);
  904.     x = min(x, xhi);
  905.     return x;
  906. }
  907.  
  908. int
  909. seen_terrain_at(x, y, side)
  910. int x, y;
  911. Side *side;
  912. {
  913.     if (in_area(x, y)
  914.     && (!side || g_see_all() || side->designer ||
  915.         terrain_view(side, x, y) != UNSEEN)) {
  916.     return terrain_at(x, y);
  917.     } else {
  918.     return NONTTYPE;
  919.     }
  920. }
  921.  
  922. Unit *
  923. seen_unit_at(x,y,side)
  924. int x, y;
  925. Side *side;
  926. {
  927.     if (!in_area(x, y))
  928.       return NULL;
  929.     if (!side || g_see_all() || side->designer || cover(side, x, y) > 0)
  930.       return unit_at(x, y);
  931.     return NULL;
  932. }
  933.  
  934. int
  935. seen_utype_at(x,y,side)
  936. int x, y;
  937. Side *side;
  938. {
  939.     Unit *unit;
  940.     short view;
  941.  
  942.     if (!in_area(x, y))
  943.       return NONUTYPE;
  944.     unit = seen_unit_at(x, y, side);
  945.     if (unit)
  946.       return unit->type;
  947.  
  948.     view = unit_view(side, x, y);
  949.     if (view != EMPTY)
  950.       return vtype(view);
  951.  
  952.     return NONUTYPE;
  953. }
  954.  
  955. int
  956. sideno_of_seen_unit_at(x,y,side)
  957. int x, y;
  958. Side *side;
  959. {
  960.    Unit *unit;
  961.    short view;
  962.  
  963.    if (!in_area(x, y))
  964.      return indepside->id;
  965.    unit = seen_unit_at(x,y,side);
  966.    if (unit)
  967.      return side_number(unit->side);
  968.    view = unit_view(side, x, y);
  969.    if (view != EMPTY)
  970.      return vside(view);
  971.    return indepside->id;
  972. }
  973.  
  974. char *
  975. name_of_seen_unit_at(x,y,side)
  976. int x, y;
  977. Side *side;
  978. {
  979.     Unit *unit;
  980.     
  981.     if (!pp->names || !in_area(x, y))
  982.       return NULL;
  983.     unit = seen_unit_at(x,y,side);
  984.     if (unit) {
  985.     if (pp->names > 1) {
  986.         name_or_number(unit, buffer);
  987.         if (buffer[0]) {
  988.         return add_esc_string(buffer);
  989.         }
  990.     } else {
  991.         if (unit->name && unit->name[0]) {
  992.         return add_esc_string(unit->name);
  993.         } 
  994.     }
  995.     } 
  996.     return NULL;
  997. }
  998.  
  999. char *
  1000. summary_of_seen_units_at(x,y,side)
  1001. int x, y;
  1002. Side *side;
  1003. {
  1004.     Unit *mainunit, *other, *occ;
  1005.     int i, nums[MAXUTYPES];
  1006.     
  1007.     if (!in_area(x, y) || !pp->cell_summary)
  1008.       return NULL;
  1009.     mainunit = seen_unit_at(x, y, side);
  1010.     if (!mainunit || (!mainunit->nexthere && !mainunit->occupant))
  1011.       return NULL;
  1012.     
  1013.     for_all_unit_types(i) {
  1014.     nums[i] = 0;
  1015.     }
  1016.     for_all_stack(x, y, other) {
  1017.     nums[other->type]++;
  1018.     for_all_occupants(other, occ) {
  1019.         nums[occ->type]++;
  1020.     }
  1021.     }
  1022.     nums[mainunit->type]--;
  1023.     
  1024.     strcpy(buffer, "(+");
  1025.     for_all_unit_types(i) {
  1026.     if (nums[i] != 0) {
  1027.         tprintf(buffer, " %d %1s,", nums[i], utype_name_n(i, 1));
  1028.     }
  1029.     }
  1030.     i = strlen(buffer);
  1031.     buffer[i-1] = ')';
  1032.     
  1033.     return add_esc_string(buffer);
  1034. }
  1035.  
  1036. /* This routine could still use a cleanup */
  1037.  
  1038. void
  1039. dump_ps_view(side, ipp, filename)
  1040. Side *side;
  1041. PrintParameters *ipp;
  1042. char *filename;
  1043. {
  1044.     int x, y, xx, i, j, t, nx, utype, page, d, cx, cy, skip, nsp;
  1045.     int f, nf, tw, th, font, named_unit_max=100, xmin, ymin, xmin1;
  1046.     int l, l1, x1, y1, x2, y2, d1, mvalue, nvalue, pad;
  1047.     FILE *fp;
  1048.     char stime[42], *name, *summary, tmpbuf[BUFSIZE];
  1049.     char *name_layer, *name_position;
  1050.     time_t tnow;
  1051.     Legend *legs = NULL, *lp;
  1052.     double length, magnif;
  1053.     
  1054.     if (ipp) {
  1055.     *pp = *ipp;
  1056.     }
  1057.     
  1058.     ps_side = (side) ? side : sidelist->next;
  1059.     misc_init();
  1060.     
  1061.     /* does this work on the MAC too? (it's supposed to be ANSI) */
  1062.     time(&tnow);
  1063.     strftime(stime, 40, "%d %b %Y", localtime(&tnow));
  1064.     
  1065.     /* compute position of legends of geographical features */
  1066.     if (pp->features) {
  1067.     nf = num_features();
  1068.     if (nf > 0) {
  1069.         legs = (Legend *) malloc((nf + 1) * sizeof(Legend));
  1070.         if (legs)
  1071.           place_feature_legends(legs, nf, ps_side, 2, 1);
  1072.     }
  1073.     }
  1074.     
  1075.     /* need to initialize and cook the image library? */
  1076.     if (img_cooked == 0) {
  1077.     init_ps_print(NULL);
  1078.     ps_cook_imf();
  1079.     }
  1080.     if (img_cooked!=1) {
  1081.     notify(ps_side, "image cooking failed, dump skipped");
  1082.     return;
  1083.     }
  1084.     
  1085.     /*  open view file  */
  1086.     fp = fopen(filename, "w");
  1087.     if (fp == NULL) {
  1088.     notify(ps_side, "Cannot open \"%s\", dump skipped", filename);
  1089.     return;
  1090.     }
  1091.     
  1092.     ps_initialize(fp);
  1093.     
  1094.     name_layer = (char *) malloc(((height+2) * (width+2) + 2) * sizeof(char));
  1095.     name_position = (char *) malloc((named_unit_max + 2) * sizeof(char));
  1096.     
  1097.     /* Loop over pages. */
  1098.     page = 0;
  1099.     for (i = 0; i < (area.height + height - 1) / height; i++) {
  1100.     for (j = 0; j < (area.width + width - 1) / width; j++) {
  1101.         xmin = j * width;  ymin = max(area.height - (i+1) * height, 0);
  1102.  
  1103.         /* look if the page can be skipped  */
  1104.  
  1105.         skip = 1;
  1106.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1107.         nx = min(width, area.width - xmin);
  1108.  
  1109.         for (xx = xmin; xx < xmin + nx; xx++) {
  1110.             x = xx - (y + 1) / 2 + x0;
  1111.             x = wrapx(x);
  1112.  
  1113.             skip &= seen_terrain_at(x, y, side) == NONTTYPE &&
  1114.               seen_utype_at(x, y, side) == NONUTYPE;
  1115.         }
  1116.         }
  1117.         if (skip)
  1118.           continue;
  1119.  
  1120.         page++;
  1121.         page_init(fp, page, i, j, stime);
  1122.  
  1123.         /* show terrain, asymmetric scale to make perfectly
  1124.            regular hexagons  */
  1125.  
  1126.         fprintf(fp, "%g setlinewidth gsave 1 %g scale %g setgray\n",
  1127.             cell_grid_width, 1 / asym,
  1128.             (pp->terrain_dither ? 0.0 : pp->terrain_gray));
  1129.  
  1130.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1131.         fprintf(fp, "gsave ");
  1132.         if ((y % 2) == 0)
  1133.           fprintf(fp, "hs ");
  1134.         nx = min(width, area.width - xmin);
  1135.         nsp = 0;
  1136.         for (xx = xmin; xx < xmin + nx; xx++) {
  1137.             x = xx - (y + 1) / 2 + x0;
  1138.             x = wrapx(x);
  1139.  
  1140.             if ((t = seen_terrain_at(x, y, side)) != NONTTYPE) {
  1141.             if (nsp > 1) {
  1142.                 fprintf(fp, "%d msp ", nsp);
  1143.             } else if (nsp == 1) {
  1144.                 fprintf(fp, "sp ");
  1145.             }
  1146.             nsp = 0;
  1147.             fprintf(fp, "t%X ", t);
  1148.             } else {
  1149.             nsp++;
  1150.             }
  1151.         }
  1152.         fprintf(fp, "grestore nl\n");
  1153.         }
  1154.         fprintf(fp, "grestore\n");
  1155.  
  1156.         /* draw borders.  (cx,cy) is the hex center */
  1157.  
  1158.         fprintf(fp, "gsave %g setlinewidth 1 setlinecap\n", border_width);
  1159.  
  1160.         cy = -hex_h/2 + hex_c;
  1161.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1162.         cy -= hex_c;
  1163.  
  1164.         cx = (1 + ((y%2)==0))*hex_w/2 - hex_w;
  1165.         nx = min(width, area.width-xmin);
  1166.         for (xx = xmin; xx < xmin + nx; xx++) {
  1167.             cx += hex_w;
  1168.             x = xx - (y + 1) / 2 + x0;
  1169.             x = wrapx(x);
  1170.             if (!in_area(x, y))
  1171.               continue;
  1172.  
  1173.             for_all_terrain_types(t) {
  1174.             if (t_is_border(t) && aux_terrain_defined(t)) {
  1175.                 for_all_directions(d) {
  1176.                 if (border_at(x, y, d, t) &&
  1177.                     (!side
  1178.                      || g_see_all()
  1179.                      || seen_border(side, x, y, d))) {
  1180.                     fprintf(fp, "%d %g %d %g li\n",
  1181.                         cx + hexc_x[d],
  1182.                         (cy + hexc_y[d]) / asym,
  1183.                         cx + hexc_x[d + 1],
  1184.                         (cy + hexc_y[d + 1]) / asym);
  1185.                 }
  1186.                 }
  1187.             }
  1188.             }
  1189.         }
  1190.         }
  1191.         fprintf(fp, "grestore\n");
  1192.  
  1193.         /* draw connections.  (cx,cy) is the hex center */
  1194.  
  1195.         fprintf(fp, "gsave %g setlinewidth 1 setlinecap\n",
  1196.             connection_width);
  1197.  
  1198.         cy = -hex_h/2 + hex_c;
  1199.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1200.         cy -= hex_c;
  1201.         cx = (1 + ((y % 2) == 0)) * hex_w / 2 - hex_w;
  1202.         nx = min(width, area.width - xmin);
  1203.         for (xx = xmin; xx < xmin + nx; xx++) {
  1204.             cx += hex_w;
  1205.             x = xx - (y + 1) / 2 + x0;
  1206.             x = wrapx(x);
  1207.             if (!in_area(x, y))
  1208.               continue;
  1209.             for_all_terrain_types(t) {
  1210.             if (t_is_connection(t) && aux_terrain_defined(t)) {
  1211.                 for_all_directions(d) {
  1212.                 if (connection_at(x, y, d, t) &&
  1213.                     (!side || g_see_all() ||
  1214.                      terrain_view(side, x, y) != UNSEEN)) {
  1215.                     fprintf(fp, "%d %g %d %g li\n",
  1216.                         cx,
  1217.                         cy / asym,
  1218.                         cx + conn_x[d],
  1219.                         (cy + conn_y[d]) / asym);
  1220.                 }
  1221.                 }
  1222.             }
  1223.             }
  1224.         }
  1225.         }
  1226.         fprintf(fp, "grestore\n");
  1227.  
  1228.         /* print feature names.  (cx,cy) is the legend center */
  1229.  
  1230.         if (pp->features && legs) {
  1231.         fprintf(fp, "gsave\n");
  1232.         th = -hex_c * (area.height - i * height - 1
  1233.                    - max(area.height - (i+1) * height,0))  - hex_h;
  1234.         tw = hex_w * min(width, area.width - xmin) + hex_w / 2;
  1235.         fprintf(fp, "newpath 0 0 moveto 0 %g lineto %d %g lineto ",
  1236.             th / asym, tw, th / asym);
  1237.         fprintf(fp, "%d 0 lineto 0 0 lineto closepath clip\n", tw);
  1238.         
  1239.         for (f = 1, lp = legs; f <= nf; f++, lp++) {
  1240.             name = feature_desc(find_feature(f), buffer);
  1241.             if (lp->dist < 0.0 || !name)
  1242.               continue;
  1243.             xx = wrap(lp->ox + (lp->oy + 1) / 2 - x0) - xmin;
  1244.             if (xx >= nx) {
  1245.             xx -= area.width;
  1246.             }
  1247.             cx = (1 + ((lp->oy%2)==0))*hex_w/2 +
  1248.               (2*lp->dx+lp->dy)*hex_w/4 + xx*hex_w;
  1249.             cy = -hex_h/2 - hex_c*(area.height-1-i*height - lp->oy) +
  1250.               lp->dy*hex_c/2;
  1251.  
  1252.             length = hex_w*(lp->dist + 1);
  1253.             magnif = (1000 * length) / ps_string_width(name, 2);
  1254.  
  1255.             /* tweak these parameters: */
  1256.             magnif *= 0.9;
  1257.             if (magnif < 6) {
  1258.             magnif = 8;
  1259.             font = 0;
  1260.             } else if (magnif < 8) {
  1261.             magnif = 8;
  1262.             font = 1;
  1263.             } else if (magnif < 20) {
  1264.             font = 2;
  1265.             } else {
  1266.             /* keep magnif near 20 */
  1267.             font = 2;
  1268.             pad = ps_string_width(name, 2)
  1269.               * (magnif / 20.0 - 1.0)
  1270.                 / (strlen(name) * ps_string_width(" ", 2)) + 0.5;
  1271.             name = pad_blanks(name, pad);
  1272.             magnif =
  1273.               (1000 * length) / ps_string_width(name, 2) * 0.9;
  1274.             }
  1275.  
  1276.             /* comment containing debugging info */
  1277.             fprintf(fp, "%% f = %d, ox = %d, oy = %d, dx = %d, dy = %d, ",
  1278.                 f, lp->ox, lp->oy, lp->dx, lp->dy);
  1279.             fprintf(fp, "angle = %g, dist = %g\n",
  1280.                 lp->angle, lp->dist);
  1281.             fprintf(fp, "/%s findfont 1 scalefont setfont ",
  1282.                 ps_name[font]);
  1283.             fprintf(fp, "%d %g moveto gsave %g rotate %g %g scale ",
  1284.                 cx, cy/asym, lp->angle, magnif, magnif);
  1285.             fprintf(fp, "0 %g rmoveto (%s) cshow grestore\n",
  1286.                 -0.0005*ps_height, add_esc_string(name));
  1287.         }
  1288.         fprintf(fp, "grestore\n");
  1289.         }
  1290.  
  1291.         /* look for name/summary places */
  1292.         if (name_layer && name_position) {
  1293.         for (l = 0; l < (height + 2) * (width + 2); l++) {
  1294.             name_layer[l] = '\0';
  1295.         }
  1296.  
  1297.         l = 0;
  1298.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1299.     
  1300.             nx = min(width, area.width - xmin);
  1301.             xmin1 = xmin - (y + 1) / 2 + x0;
  1302.             for (xx = xmin; xx < xmin + nx; xx++) {
  1303.             x = xx - (y + 1) / 2 + x0;
  1304.  
  1305.             /* we don't need the string here, so can overwrite */
  1306.             name = name_of_seen_unit_at(wrapx(x), y, side);
  1307.             summary = summary_of_seen_units_at(wrapx(x), y, side);
  1308.  
  1309.             if (!name && !summary)
  1310.               continue;
  1311.             l++;
  1312.             if (l > named_unit_max) {
  1313.                 named_unit_max *= 2;
  1314.                 name_position = (char *)
  1315.                   realloc(name_position,
  1316.                       (named_unit_max + 2) * sizeof(char));
  1317.             }
  1318.             name_position[l] = '\177';
  1319.  
  1320.             /* choose the free nearby hex with less named units around */
  1321.             nvalue = 99;
  1322.             for (l1 = 0; l1 < NUMDIRS; l1++) {
  1323.                 d = name_dir[l1];
  1324.                 point_in_dir(x, y, d, &x1, &y1);
  1325.                 if ((seen_unit_at(wrapx(x1), y1, side)
  1326.                  && on_page(x1 - xmin1, y1 - ymin))
  1327.                 || (name_at(x1 - xmin1, y1 - ymin) & N_BK))
  1328.                   continue;
  1329.                 mvalue = 0;
  1330.                 for_all_directions(d1) {
  1331.                 point_in_dir(x1, y1, d1, &x2, &y2);
  1332.                 if ((name_of_seen_unit_at(wrapx(x2), y2, side)
  1333.                      || summary_of_seen_units_at(wrapx(x2),
  1334.                                  y2, side))
  1335.                     && on_page(x2 - xmin1, y2 - ymin))
  1336.                   mvalue++;
  1337.                 }
  1338.                 if (mvalue<nvalue) {
  1339.                 name_position[l] = d;
  1340.                 nvalue = mvalue;
  1341.                 }
  1342.             }
  1343.             
  1344.             d = name_position[l];
  1345.             if (d < NUMDIRS && d >= 0) {
  1346.                 point_in_dir(x, y, d, &x1, &y1);
  1347.                 name_at(x1 - xmin1, y1 - ymin) |= N_BK;
  1348.                 /* comment containing debugging info */
  1349.                 fprintf(fp, "%% found pos for %s in (%d,%d) at (%d,%d), %s\n",
  1350.                     name_of_seen_unit_at(wrapx(x),y,side), x,y,
  1351.                     x1, y1, dirnames[d]);
  1352.             }
  1353.             }
  1354.         }
  1355.         
  1356.         /* print unit names.  (cx,cy) is the upper-left corner
  1357.            of the rectangle containing the hex */
  1358.  
  1359.         fprintf(fp, "gsave\n");
  1360.         fprintf(fp, "/%s findfont 8 scalefont setfont\n", ps_name[1]);
  1361.  
  1362.         l = 0;
  1363.         cy = hex_c;
  1364.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1365.             cy -= hex_c;
  1366.             
  1367.             cx = ((y % 2) == 0) * hex_w / 2 - hex_w;
  1368.             nx = min(width, area.width-xmin);
  1369.             xmin1 = xmin - (y+1)/2 + x0;
  1370.             for (xx = xmin; xx < xmin+nx; xx++) {
  1371.             cx += hex_w;
  1372.             x = xx - (y+1)/2 + x0;
  1373.  
  1374.             name = name_of_seen_unit_at(wrapx(x),y,side);
  1375.             /* free buffer */
  1376.             if (name) {
  1377.                 strcpy(tmpbuf,name);
  1378.                 name = tmpbuf;
  1379.             }
  1380.             summary = summary_of_seen_units_at(wrapx(x),y,side);
  1381.  
  1382.             if (!name && !summary)
  1383.               continue;
  1384.             l++;
  1385.  
  1386.             if (name_position[l]=='\177')
  1387.               continue;
  1388.             /* Good position found, otherwise delay until
  1389.                second round. */
  1390.             d = name_position[l];
  1391.             if (d < 0 || d >= NUMDIRS) {
  1392.                 name_position[l] = '\177';
  1393.                 continue;
  1394.             }
  1395.             point_in_dir(x, y, d, &x1, &y1);
  1396.             /* comment containing debugging info */
  1397.             fprintf(fp, 
  1398.                 "%% calling print_unit_legends(fp, \"%s\", \"%s\", \\x%2.2x, %s)\n",
  1399.                 name ? name : "", summary ? summary : "",
  1400.                 name_at(x1-xmin1,y1-ymin),
  1401.                 dirnames[(int) name_position[l]]);
  1402.             if (!print_unit_legends(fp, name, summary,
  1403.                         &(name_at(x1 - xmin1, y1 - ymin)),
  1404.                         (int) name_position[l], cx, cy))
  1405.               name_position[l] = '\127';
  1406.             }
  1407.         }
  1408.         }
  1409.         fprintf(fp, "grestore\n");
  1410.  
  1411.         /* print remaining unit names.  (cx,cy) is the upper-left corner
  1412.            of the rectangle containing the hex */
  1413.  
  1414.         fprintf(fp, "gsave\n");
  1415.  
  1416.         l = 0;
  1417.         cy = hex_c;
  1418.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1419.         cy -= hex_c;
  1420.         
  1421.         cx = ((y%2)==0)*hex_w/2 - hex_w;
  1422.         nx = min(width, area.width-xmin);
  1423.         for (xx = xmin; xx < xmin + nx; xx++) {
  1424.             cx += hex_w;
  1425.             x = xx - (y + 1) / 2 + x0;
  1426.             x = wrapx(x);
  1427.             
  1428.             name = name_of_seen_unit_at(x, y, side);
  1429.             /* free buffer */
  1430.             if (name) {
  1431.             strcpy(tmpbuf,name);
  1432.             name = tmpbuf;
  1433.             }
  1434.             summary = summary_of_seen_units_at(x,y,side);
  1435.             if (!name)
  1436.               name = summary;
  1437.             if (!name)
  1438.               continue;
  1439.             l++;
  1440.  
  1441.             if ((name_layer
  1442.              && name_position
  1443.              && name_position[l] == '\177')
  1444.             || !name_layer
  1445.             || !name_position) {
  1446.             /* choose font */
  1447.             if (8 * ps_string_width(name, 1) > 1000 * hex_w) {
  1448.                 font = 0;
  1449.             } else {
  1450.                 font = 1;
  1451.             }
  1452.             fprintf(fp, "/%s findfont 8 scalefont setfont\n",
  1453.                 ps_name[font]);
  1454.             
  1455.             fprintf(fp, "gsave %d %g translate (%s) n grestore\n",
  1456.                 cx, cy/asym, name);
  1457.             }
  1458.         }
  1459.         }
  1460.         fprintf(fp, "grestore\n");
  1461.  
  1462.         /* show units & side emblems.  (cx,cy) is the upper-left corner
  1463.            of the rectangle containing the hex */
  1464.  
  1465.         fprintf(fp, "gsave\n");
  1466.  
  1467.         cy = hex_c;
  1468.         for (y = area.height - 1 - i * height; y >= ymin; y--) {
  1469.         cy -= hex_c;
  1470.     
  1471.         cx = ((y % 2) == 0) * hex_w / 2 - hex_w;
  1472.         nx = min(width, area.width - xmin);
  1473.         for (xx = xmin; xx < xmin + nx; xx++) {
  1474.             cx += hex_w;
  1475.             x = xx - (y + 1) / 2 + x0;
  1476.             x = wrapx(x);
  1477.             if (!in_area(x, y))
  1478.               continue;
  1479.     
  1480.             if ((utype = seen_utype_at(x,y,side)) != NONUTYPE) {
  1481.             fprintf(fp, "gsave %d %g translate u%X s%X grestore\n",
  1482.                 cx, cy / asym, utype,
  1483.                 sideno_of_seen_unit_at(x, y, side));
  1484.             }
  1485.         }
  1486.         }
  1487.         fprintf(fp, "grestore\n");
  1488.  
  1489.         fprintf(fp, "showpage\n");
  1490.     }
  1491.     }
  1492.     /* End of loop over pages. */
  1493.     
  1494.     fprintf(fp, "%%%%Trailer\n");
  1495.     fprintf(fp, "%%%%Pages: %d\n", page);
  1496.     fprintf(fp, "%%%%EOF\n");
  1497.     
  1498.     fclose(fp);
  1499.     if (legs)
  1500.       free(legs);
  1501.     if (name_layer)
  1502.       free(name_layer);
  1503.     if (name_position)
  1504.       free(name_position);
  1505. }
  1506.  
  1507. /* Compute the width of a PostScript string, in thousands of the
  1508.    "nominal size". */
  1509.  
  1510. int 
  1511. ps_string_width(str, font)
  1512. char *str;
  1513. int font;
  1514. {
  1515.     int i = 0, esc = 0;
  1516.     
  1517.     if (!str)
  1518.       return 0;
  1519.     
  1520.     for (; *str; str++) {
  1521.     if (*str=='\\' && !esc) {
  1522.         esc = 1;
  1523.     } else {
  1524.         i += ps_width[font][(int) *str];
  1525.         esc = 0;
  1526.     }
  1527.     }
  1528.     return i;
  1529. }
  1530.  
  1531. /* This routine can be used by the interface to place legends */
  1532.  
  1533. /* orient==0 :  E (horizontal) only;
  1534.    orient==1 :  E, SE, NE;
  1535.    orient==2 :  E, SE, NE, ESE, ENE, N; */
  1536.  
  1537. /* block==0  :  write over any unit;
  1538.    block==1  :  don't write over "city-like" units;
  1539.    block==2  :  don't write over visible units. */
  1540.  
  1541. void
  1542. place_feature_legends(leg, nf, side, orient, block)
  1543. Legend *leg;
  1544. int nf;
  1545. Side *side;
  1546. int orient, block;
  1547. {
  1548.   int x, y, x1, y1, dx, dy, f, i, i3, id, d, nd, d1, dc;
  1549.     double dist;
  1550.     static int ndt[] = { 1, 3, 6 },
  1551.     dt[] = { EAST, SOUTHEAST, NORTHEAST, NORTHEAST, SOUTHEAST, EAST },
  1552.     da[] = { 0, -60, 60, 90, -30, 30 };
  1553.   unsigned char *auxf_layer, dmask;
  1554.     
  1555.     if (!features_defined()) return;
  1556.     
  1557.     orient = min(orient, 2);
  1558.     nd = ndt[orient];
  1559.     
  1560.     for (f = 1; f <= nf; f++) {
  1561.     leg[f-1].ox = 0;
  1562.     leg[f-1].oy = 0;
  1563.     leg[f-1].dx = 0;
  1564.     leg[f-1].dy = 0;
  1565.     leg[f-1].angle = 0;
  1566.     leg[f-1].dist  = -1;
  1567.     }
  1568.     
  1569.   /* Speedup: in auxf_layer we keep this information:
  1570.      the cell is unseen or hosts a blocking unit (bit 7);
  1571.      the cell has already been reached from direction id (bit id)
  1572.      [this avoids repeating the same path over and over;
  1573.       note that directions 3,4,5 zig-zag with step 3,
  1574.       so this bit is set/checked only every 3 steps.] */
  1575.  
  1576.   auxf_layer = (unsigned char *)
  1577.       malloc(area.height*area.width*sizeof(unsigned char));
  1578.  
  1579.   if (!auxf_layer)  return;
  1580.  
  1581.     for_all_cells(x,y) {
  1582.     if (seen_terrain_at(x,y,side) == NONTTYPE ||
  1583.      blocking_utype(seen_utype_at(x,y,side),block)) {
  1584.       aset(auxf_layer,x,y, '\x80');
  1585.     } else {
  1586.       aset(auxf_layer,x,y, '\0');
  1587.     }
  1588.   }
  1589.  
  1590.   for_all_cells(x,y) {
  1591.     f = raw_feature_at(x,y);
  1592.     if (f<1 || f>nf)
  1593.       continue;
  1594.  
  1595.     for (id = 0; id < nd; id++) {
  1596.       dmask = '\x01' << id;
  1597.         d = dt[id];
  1598.         d1 = (id<3) ? d : left_dir(left_dir(d));
  1599.         x1 = x;  y1 = y;
  1600.         dx = dy = 0;
  1601.       i3 = i = 0;
  1602.         dist = 0;
  1603.       while (raw_feature_at(x1,y1) == f &&
  1604.          !(aref(auxf_layer,x1,y1) &
  1605.            ((id<3 || !i3) ? ('\x80' | dmask) : '\x80'))) {
  1606.     if (dist > leg[f-1].dist && (id<3 || !i3)) {
  1607.             leg[f-1].ox =  x;  leg[f-1].oy =  y;
  1608.             leg[f-1].dx = dx;  leg[f-1].dy = dy;
  1609.             leg[f-1].angle = da[id];
  1610.             leg[f-1].dist  = dist;
  1611.         }
  1612.     if (id<3 || !i3) {
  1613.       auxf_layer[area.width*y1 + x1] |= dmask;
  1614.     }
  1615.     dc = (i3 == 1) ? d1 : d;
  1616.         dx += dirx[dc];
  1617.         x1 = wrap(x1 + dirx[dc]);
  1618.         dy += diry[dc];
  1619.         y1 += diry[dc];
  1620.     dist += (id<3) ? 1.0 : (i3 ? 0.5*sqrt3 : 0.0);
  1621.         i++;
  1622.     i3 = i % 3;
  1623.         }
  1624.     }
  1625.     }
  1626.  
  1627.   free(auxf_layer);
  1628. }
  1629.  
  1630. int
  1631. blocking_utype(u, block)
  1632. int u, block;
  1633. {
  1634.     if (u == NONUTYPE || block == 0)
  1635.       return 0;
  1636.     if (block > 1)
  1637.       return 1;
  1638.     /* block==1:  only visible see-always unmovable units */
  1639.     return ((u_already_seen(u)>99 || u_see_always(u)) && !mobile(u));
  1640. }
  1641.  
  1642. int 
  1643. num_features()
  1644. {
  1645.     int f = 0, x, y;
  1646.  
  1647.     if (!features_defined())
  1648.       return 0;
  1649.  
  1650.     for_all_cells(x,y) {
  1651.     f = max(f, raw_feature_at(x,y));
  1652.     }
  1653.     return f;
  1654. }
  1655.  
  1656. int
  1657. print_unit_legends(fp, name, summary, m, dir, cx, cy)
  1658. FILE *fp;
  1659. char *name, *summary, *m;
  1660. int dir, cx, cy;
  1661. {
  1662.     int left, wide_name, wide_summ, down;
  1663.     
  1664.     if (dir < 0 || dir >= NUMDIRS)
  1665.       return 0;
  1666.     
  1667.     cx += hex_w*dirx[dir] + hex_w/2*diry[dir];  cy += hex_c*diry[dir];
  1668.     if (dir == EAST) cx -= 4;
  1669.     if (dir == WEST) cx += 4;
  1670.     if (dir == NORTHEAST || dir == NORTHWEST) cy -= 1;
  1671.     if (dir == SOUTHEAST || dir == SOUTHWEST) cy += 4;
  1672.     
  1673.     left = dir == EAST || dir == NORTHEAST || dir == SOUTHEAST;
  1674.     wide_name = 8*ps_string_width(name, 1) > 1000*hex_w;
  1675.     wide_summ = 8*ps_string_width(summary, 1) > 1000*hex_w;
  1676.     
  1677.     if (name) {
  1678.     if (summary && !(*m & N_U)) {
  1679.         down = 0;
  1680.     } else if (!(*m & N_U) && !(*m & N_D)) {
  1681.         down = dir!=SOUTHEAST && dir!=SOUTHWEST;
  1682.     } else if (!(*m & N_D)) {
  1683.         down = 1;
  1684.     } else if (!(*m & N_U)) {
  1685.         down = 0;
  1686.     } else {
  1687.         return 0;
  1688.     }
  1689.     if (!down && !(*m & N_D) && summary) {
  1690.         if (dir == NORTHEAST || dir == NORTHWEST) {
  1691.         cy -= 3;
  1692.         } else if (dir == EAST || dir == WEST) {
  1693.         cy -= 2;
  1694.         }
  1695.     }
  1696.  
  1697.     fprintf(fp, "gsave %d %g translate (%s) n%c%c grestore\n",
  1698.         cx, cy/asym, name, left ? 'l' : 'r', down ? 'd' : 'u');
  1699.     *m |= down ? N_D : N_U;
  1700.     }
  1701.     
  1702.     if (summary) {
  1703.     if (name)  cy += 3;
  1704.     if (!(*m & N_U) && !(*m & N_D)) {
  1705.         down = dir!=SOUTHEAST && dir!=SOUTHWEST;
  1706.     } else if (!(*m & N_D)) {
  1707.         down = 1;
  1708.     } else if (!(*m & N_U)) {
  1709.         down = 0;
  1710.     } else {
  1711.         return 1;
  1712.     }
  1713.  
  1714.     fprintf(fp, "gsave %d %g translate (%s) n%c%c grestore\n",
  1715.         cx, cy/asym, summary, left ? 'l' : 'r', down ? 'd' : 'u');
  1716.     *m |= down ? N_D : N_U;
  1717.     }
  1718.     return 1;
  1719. }
  1720.